package com.sikaryofficial.backend.manager;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sikaryofficial.backend.annotation.TenantFilter;
import com.sikaryofficial.backend.constant.EmailConstant;
import com.sikaryofficial.backend.constant.EmailSendSourceEnum;
import com.sikaryofficial.backend.constant.PointsConstant;
import com.sikaryofficial.backend.domain.dto.req.points.PointsDetailQueryReq;
import com.sikaryofficial.backend.domain.dto.resp.points.PointsDetailResp;
import com.sikaryofficial.backend.domain.entity.PointsBalance;
import com.sikaryofficial.backend.domain.entity.PointsDetail;
import com.sikaryofficial.backend.domain.entity.PointsEmail;
import com.sikaryofficial.backend.domain.mapping.PointsDetailMapping;
import com.sikaryofficial.backend.mapper.PointsDetailMapper;
import com.sikaryofficial.backend.message.body.ScanRegisterMessage;
import com.sikaryofficial.backend.message.product.ProductProcess;
import com.sikaryofficial.backend.service.IPointsBalanceService;
import com.sikaryofficial.backend.service.IPointsDetailService;
import com.sikaryofficial.backend.service.IPointsEmailService;
import com.sikaryofficial.backend.service.email.EmailService;
import com.sikaryofficial.backend.utils.CalendarUtils;
import com.sikaryofficial.common.core.constant.PointSourceCodeEnum;
import com.sikaryofficial.common.core.domain.R;
import com.sikaryofficial.common.security.utils.SecurityUtils;
import com.sikaryofficial.system.api.RemoteDictService;
import com.sikaryofficial.system.api.RemoteUserService;
import com.sikaryofficial.system.api.domain.SysDictData;
import com.sikaryofficial.system.api.model.ScanRegisterVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.MessageFormat;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;


/**
 * @author : qinjinyuan
 * @desc : 积分相关处理
 * @date : 2024/01/10 14:36
 */
@Service
@Slf4j
public class PointsBalanceManager {
    @Autowired
    private ProductProcess productProcess;
    @Autowired
    private IPointsEmailService pointsEmailService;
    @Autowired
    private IPointsBalanceService pointsBalanceService;
    @Autowired
    private IPointsDetailService pointsDetailService;
    @Resource(name = "asyncTaskExecutor")
    private ThreadPoolTaskExecutor threadPoolExecutor;
    @Resource
    private Map<String, EmailService> emailServiceMap;

    @Resource
    private RemoteDictService dictService;

    @Autowired
    private RemoteUserService remoteUserService;
    @Autowired
    private PointsDetailMapper detailMapper;

    public boolean scanRegister(ScanRegisterVO scanRegisterVO, String brand) {
        Long userId = scanRegisterVO.getUserId();
        log.info("scanRegister,userId:{}", userId);
        if (Objects.isNull(userId) || userId <= 0L) {
            return false;
        }
        ScanRegisterMessage message = new ScanRegisterMessage();
        // 设置字段
        message.setBrand(brand);
        message.setUserId(userId);
        message.setPoints(scanRegisterVO.getPoints());
        log.info("scanRegister,message:{}", message);
        return productProcess.sendScanRegisterMessage(message);
    }

    /**
     * 积分重置邮件通知
     *
     * @return
     */
    @TenantFilter
    public boolean autoSendResetEmailNotice() {
        Long userId = 0L;
        PointsEmail maxUserData = pointsEmailService.getOne(new LambdaQueryWrapper<PointsEmail>()
                .select(PointsEmail::getUserId)
                .eq(PointsEmail::getSendYear, DateUtil.thisYear())
                .orderByDesc(PointsEmail::getUserId)
                .last("limit 1")
        );
        if (Objects.nonNull(maxUserData)) {
            userId = maxUserData.getUserId();
        }
        log.info("autoSendResetEmailNotice curMaxUserId:{}", userId);
        List<PointsBalance> needResetPoints = pointsBalanceService.list(new LambdaQueryWrapper<PointsBalance>()
                .eq(PointsBalance::getDeletedVersion, 0L)
                .gt(PointsBalance::getCreatedBy, userId)
                .orderByAsc(PointsBalance::getCreatedBy)
                .last(" limit 10")
        );
        if (CollUtil.isEmpty(needResetPoints)) {
            return false;
        }
        // 待通知列表
        List<Long> needResetUserIds = needResetPoints.stream().map(PointsBalance::getCreatedBy).distinct().collect(Collectors.toList());
        log.info("autoSendResetEmailNotice begin:{}", needResetUserIds);
        // 使用CompletableFuture.allOf等待所有任务完成
        // 等待所有任务执行完
        CompletableFuture[] futures = needResetUserIds.stream().map(item ->
                CompletableFuture.runAsync(() -> autoSendEmailNotice(item), threadPoolExecutor)).toArray(CompletableFuture[]::new);
        CompletableFuture.allOf(futures).join();
        log.info("autoSendResetEmailNotice begin:{}", needResetUserIds);
        return true;
    }

    private String buildContent(String userName) {
        String content = "  Hello {0}, Unused points will be automatically reset to zero on the last day of each year. you can attend some activity with it";
        return MessageFormat.format(content, userName);
    }

    private void autoSendEmailNotice(Long userId) {
        log.info("start--autoSendResetEmailNotice---getActivityResultId:{}", userId);
        // 发送邮件及缓存邮箱对象
        String subject = "Email Points Reset Notice Not Replay";

        AtomicReference<String> emailAddress = new AtomicReference<>("");
        AtomicReference<String> userName = new AtomicReference<>("");
        Optional.ofNullable(remoteUserService.getUserByUserId(userId)).ifPresent(user -> {
            emailAddress.set(user.getData().getEmail());
            userName.set(user.getData().getUserName());
        });
        if (CharSequenceUtil.isBlank(emailAddress.get())) {
            log.info("start--autoSendResetEmailNotice---autoSendEmailNotice:未获取到个人邮箱地址");
            return;
        }
        String content = buildContent(userName.get());
        EmailService emailService = emailServiceMap.get(EmailSendSourceEnum.NORMAL.getSendSource() + EmailConstant.SUFFIX);
        emailService.sendHtmlMail(emailAddress.get(), subject, content);
        // 发送插入已发送数据
        pointsEmailService.save(PointsEmail.builder()
                .pointsEmailId(IdWorker.getId())
                .sendYear(DateUtil.thisYear())
                .sendFlag(1)
                .userId(userId)
                .createdBy(0L)
                .createdTime(DateUtil.date())
                .build());
        log.info("end--autoSendResetEmailNotice---getActivityResultId");
    }

    @TenantFilter
    public boolean autoResetUnusedPoints() {
        // 生成明细
        detailMapper.insertResetDetail();
        // 重置积分
        return pointsBalanceService.update(new LambdaUpdateWrapper<PointsBalance>()
                .eq(PointsBalance::getDeletedVersion, 0L)
                .set(PointsBalance::getBalance, 0L)
                .set(PointsBalance::getUpdatedBy, 0L)
                .set(PointsBalance::getUpdatedTime, DateUtil.date())
                .setSql("updated_time = now()")
        );
    }

    /**
     * 分页接口
     *
     * @return
     */
    public IPage<PointsDetailResp> pointFlow(PointsDetailQueryReq req) {
        Long userId = SecurityUtils.getUserId();
        if (Objects.isNull(userId) || userId <= 0) {
             return new Page<>(req.getPageNum(), req.getPageSize(), 0);
        }
        Page<PointsDetail> customerPage = new Page<>(req.getPageNum(), req.getPageSize());
        LambdaQueryWrapper<PointsDetail> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(PointsDetail::getDeletedVersion, 0L);
        if (Objects.nonNull(req.getYear()) && Objects.nonNull(req.getMonth())) {
            queryWrapper.between(PointsDetail::getCreatedTime, CalendarUtils.getFirstDayOfMonth(req.getYear(), req.getMonth()), CalendarUtils.getLastDayOfMonth(req.getYear(), req.getMonth()));
        }
        queryWrapper.eq(PointsDetail::getCreatedBy, userId);
        queryWrapper.eq(PointsDetail::getHasValid, 0);
        queryWrapper.orderByDesc(PointsDetail::getCreatedTime);
        IPage<PointsDetail> pointsDetailPage = pointsDetailService.page(customerPage, queryWrapper);
        IPage<PointsDetailResp> resultPage =  pointsDetailPage.convert(PointsDetailMapping.INSTANCE::coverToResp);
        R<List<SysDictData>> dictDataResult = dictService.dictType(PointsConstant.POINTS_RULE_SOURCE_PATH_DICT_TYPE);
        List<SysDictData> dictDataList = dictDataResult.getData();
        // 积分路径字典
        Map<String, String> ruleSourcePathMap = dictDataList.stream().collect(Collectors.toMap(SysDictData::getDictValue, SysDictData::getDictLabel));

        // 积分来源描述设值
        resultPage.getRecords().forEach(item-> {
            item.setSourceCodeDesc(PointSourceCodeEnum.getDescByCode(item.getSourceCode()));
            item.setSourcePath(ruleSourcePathMap.get(item.getSourceCode()));
            if (item.getOpt().equals(1)) {
                item.setSourcePath(item.getSourceName());
            }
            item.setYearMonth(DateUtil.format(item.getCreatedTime(), "yyyy-MM"));
        });
        return resultPage;
    }
}
